package com.sample.dubbo.demo.consumer.task;

import com.sample.dubbo.demo.api.CustomerService;
import com.sample.dubbo.demo.dto.Customer;
import com.sample.dubbo.demo.dto.QuerySchema;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.Instant;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;

public class Task implements Runnable {
    private static final Logger logger = LoggerFactory.getLogger(Task.class);
    int sequence;
    CustomerService customerService;
    AtomicInteger success;
    AtomicInteger failed;

    private int timeout;
    private int queryTimeout;

    public Task(int sequence, CustomerService customerService, AtomicInteger success, AtomicInteger failed, int timeout, int queryTimeout) {
        this.sequence = sequence;
        this.customerService = customerService;
        this.success = success;
        this.failed = failed;
        this.timeout = timeout;
        this.queryTimeout = timeout + queryTimeout;
    }

    @Override
    public void run() {
        try {
            long id = sequence;
            watch(this::create, id);
            watch(this::query);
            watch(this::find, id);
            watch(this::delete, id);
            success.incrementAndGet();
        } catch (Throwable e) {
            String errorMsg = e.getMessage();
            if (e instanceof NullPointerException) {
                logger.error("null", e);
            }
            if (errorMsg == null || errorMsg.length() == 0) {
                logger.error(e.getClass().getName());
            } else {
                int end = errorMsg.indexOf('\n');
                if (end > 0) {
                    logger.error(errorMsg.substring(0, end));
                } else {
                    logger.error(errorMsg);
                }
            }
            failed.incrementAndGet();
        }
    }

    private void watch(Runnable runnable) {
        long s = Instant.now().toEpochMilli();
        runnable.run();
        long e = Instant.now().toEpochMilli();
        long actual = e - s;
        if (actual > queryTimeout) {
            logger.warn("timeout actual: {} expect: {}", actual, queryTimeout);
        }
    }

    private void watch(Consumer<Long> consumer, Long id) {
        long s = Instant.now().toEpochMilli();
        consumer.accept(id);
        long e = Instant.now().toEpochMilli();
        long actual = e - s;
        if (actual > timeout) {
            logger.warn("timeout actual: {} expect: {}", actual, timeout);
        }
    }

    private void create(Long id) {
        Customer customer = Customer.createById(sequence);
        Customer customer2 = customerService.save(customer);
        if (customer2.getCode().equals(customer.getCode())) {
            logger.info("create customer success, code: " + customer.getCode());
        } else {
            logger.error("create customer code not equal");
        }
    }

    private void query() {
        QuerySchema schema = new QuerySchema();
        schema.setCondition("select all");
        List<Customer> customers = customerService.query(schema);
        if (customers == null || customers.size() == 0) {
            logger.error("query customer failed");
        } else {
            logger.info("query customer success, count: " + customers.size());
        }
    }

    private void find(Long id) {
        Customer customer = customerService.find(id);
        if (customer == null || customer.getId() != id) {
            logger.info("find customer success, code: " + customer.getCode());
        } else {
            logger.error("find customer failed");
        }
    }

    private void delete(Long id) {
        boolean b = customerService.delete(id);
        if (b) {
            logger.info("delete customer success");
        } else {
            logger.error("delete customer failed");
        }
    }
}
